﻿#include "FtpDownloader.h"
#include "DownloadController.h"
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QSslSocket>
#include <QFile>
#include <QDebug>

QMutex fileMutex;
FtpDownloader::FtpDownloader(int startPoint, int endPoint ,int index, int speed, QObject* parent)
              :QObject(parent)
{
    m_StartPoint = startPoint;
    m_EndPoint = endPoint;
    m_speed = speed;
    m_Index =index;
    m_HaveDoneBytes = 0;
    m_CurrentRecvBytes = 0;

}

FtpDownloader::~FtpDownloader()
{

}

//写给服务器信息
int FtpDownloader::excuteCmd(std::string cmd) {
    cmd += "\r\n";
    m_commandSocket->write(cmd.c_str());
    return 0;
}

//连接套接字将服务器响应存入缓冲区
int FtpDownloader::recvControl(int stateCode, std::string errorInfo){
    if(errorInfo.size()==1)
        errorInfo = "state code error!";
    if(nextInfo.size()==0) {
        int t;
        QThread::msleep(50);
        // 清空缓存和响应消息
        memset(buf, 0, BUFLEN);
        recvInfo.clear();
        m_commandSocket->waitForReadyRead();
        // 读取服务器响应
        std::int64_t infolen = m_commandSocket->read(buf, BUFLEN);
        if(infolen==BUFLEN) {
            // 消息过长
           return -1;
        }
        buf[infolen] = '\0';
        t = getStateCode();
        // 将接收到的响应消息存储在recvInfo中
        recvInfo = buf;
        //确定文件传输是否完成
        int temp = recvInfo.find("\r\n226");
        if(temp>=0) {
            // 下一次响应信息
            nextInfo = recvInfo.substr(temp+2);
        }
        if(t == stateCode)
            return 0;
        else {
            return -1;
        }
    }
    else {
        recvInfo = nextInfo;
        nextInfo.clear();
        return 0;
    }
}

//检测被动模式下状态码
int FtpDownloader::getStateCode()
{
    int num=0;
    char* p = buf;
    while(p != nullptr)
    {
        num=10*num+(*p)-'0';
        p++;
        if(*p==' ' || *p=='-')
        {
            break;
        }
    }
    return num;
}

//被动模式下的数据连接的端口号,(h1,h2,h3,h4,p1,p2)
//(p1,p2)表示数据传输端口号，客户端需要将这两个数字转换为一个16位的整数值（即端口号）
int FtpDownloader::getPortNum()
{
    int num1=0,num2=0;
    //响应缓存区
    char* p=buf;
    int cnt=0;
    while( 1 )
    {
        if(cnt == 4 && (*p) != ',')
        {
            if(*p<='9' && *p>='0')
                num1 = 10*num1+(*p)-'0';
        }
        if(cnt == 5)
        {
            if(*p<='9' && *p>='0')
                num2 = 10*num2+(*p)-'0';
        }
        if((*p) == ',')
        {
            cnt++;
        }
        p++;
        if((*p) == ')')
        {
            break;
        }
    }
    return num1*256+num2;

}

int FtpDownloader::intoPasv() {
    //切换到被动模式
    excuteCmd("PASV");
    recvControl(227);
    //executeFTPCmd(227, "PASV");                //227

    //返回的信息格式为---h1,h2,h3,h4,p1,p2
    //其中h1,h2,h3,h4为服务器的地址，p1*256+p2为数据端口
    dataPort = getPortNum();
    //客户端数据传输socket
    m_dataSocket = new QTcpSocket;
    m_dataSocket->connectToHost(m_Url.host(), dataPort);
    return 0;
}

// 获取当前下载进度
void FtpDownloader::getCurrentDownProgress(int& currentSize, int& totalSize)
{
    currentSize = m_HaveDoneBytes;
    totalSize = m_EndPoint - m_StartPoint;
}

// 获取当前状态
FtpDownloader::DownloadStatus FtpDownloader::getCurrentStatus(void)
{
    return m_status;
}

// 开始分片下载
void FtpDownloader::startDownload(const QUrl url)
{
    isDone = false;
    m_Url = url;
    m_userName = std::string(url.userName().toLocal8Bit());
    m_password = std::string(url.password().toLocal8Bit());
    //m_Qnam = new QNetworkAccessManager;



    m_commandSocket = new QTcpSocket;
    m_commandSocket->connectToHost(m_Url.host(), m_Url.port());
    QThread::msleep(300);
    if(0 != recvControl(220)){

    }
    excuteCmd("USER " + m_userName);
    if(0 != recvControl(331)){
        QString msg("FTP Client USER Error");
        emit error(msg);
        return;
    }
    excuteCmd("PASS " + m_password);
    if(0 != recvControl(230)){
        QString msg("FTP Client PASSWORD Error");
        emit error(msg);
        return;
    }
    //m_dataSocket被动模式连接
    intoPasv();
    // 设置进入流模式，文件类型为binary
    excuteCmd("TYPE I");
    recvControl(200);
    excuteCmd("MODE S");
    recvControl(200);

    // 写入读取字节范围
    std::string restStr = std::string("REST "+ std::to_string(m_StartPoint));
    std::string retrStr = std::string("RETR "+ m_Url.fileName().toStdString());
    excuteCmd(restStr);
    //从当前字节开始读取
    excuteCmd(retrStr);

    connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(onReplyReadyRead()));
    connect(m_commandSocket, SIGNAL(readyRead()), this, SLOT(onControlReadyRead()));

    //开启限速检测
    if(m_speed != 0)
    {
        m_pTimer = new QTimer(this);
        QObject::connect(m_pTimer, SIGNAL(timeout()), this, SLOT(onTimeout()));
        m_pTimer->start(10);
    }
}

void FtpDownloader::onStart(QUrl url, QFile* file)
{
    m_File = file;
    startDownload(url);
}

// 暂停下载
void FtpDownloader::onStop()
{
    m_status = t_Pause;
    qDebug() << m_Index << ", " << "Pause";
    m_commandSocket->close();
    m_commandSocket->deleteLater();
    m_dataSocket->close();
    m_dataSocket->deleteLater();

    // 使用条件变量使线程进入休眠
    m_mutex.lock();
    m_condition.wait(&m_mutex);
    m_mutex.unlock();

    onRestart();
    m_status = t_Downloading;
}

void FtpDownloader::onRestart()
{
    m_commandSocket = new QTcpSocket;
    m_commandSocket->connectToHost(m_Url.host(), m_Url.port());
    QThread::msleep(300);

    //FTP重连
    recvControl(220);
    excuteCmd("USER " + m_userName);
    recvControl(331);
    excuteCmd("PASS " + m_password);
    recvControl(230);
    intoPasv();
    excuteCmd("TYPE I");
    recvControl(200);
    excuteCmd("MODE S");
    recvControl(200);

    std::string restStr = std::string("REST "+ std::to_string(m_StartPoint + m_HaveDoneBytes));
    std::string retrStr = std::string("RETR "+ m_Url.path().toStdString());
    excuteCmd(restStr); // 断点续传
    recvControl(350);
    excuteCmd(retrStr);
    recvControl(150);

    connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(onReplyReadyRead()));
    //connect(m_dataSocket, SIGNAL(finished()), this, SLOT(onReplyFinished()));
    connect(m_commandSocket, SIGNAL(readyRead()), this, SLOT(onControlReadyRead()));
}

//删除Socket
void FtpDownloader::onDelete()
{
    if(m_commandSocket != nullptr)
    {
        m_commandSocket->close();
        m_commandSocket->deleteLater();
    }
    if(m_dataSocket != nullptr)
    {
        m_dataSocket->close();
        m_dataSocket->deleteLater();
    }
}

// 每10ms唤醒一次
void FtpDownloader::onTimeout(void)
{
    if(m_status==t_Finished)
        return ;
    int delta = m_HaveDoneBytes - m_CurrentRecvBytes;
    m_CurrentRecvBytes = m_HaveDoneBytes;
    //qDebug() << QTime::currentTime().toString(Qt::ISODate) << ": "  << m_Index << ": " << m_Reply->thread()->currentThreadId() \
             << ": " << m_Qnam->thread()->currentThreadId() << ": " << delta;
    //超过限制速度，休眠
    if(delta > m_speed/100)
    {
        double temp = (((delta - m_speed/100)/ (double)m_speed)*1000);
        m_commandSocket->close();
        m_commandSocket->deleteLater();
        m_dataSocket->close();
        m_dataSocket->deleteLater();
        QThread::msleep((int)temp);

        m_commandSocket = new QTcpSocket;
        m_commandSocket->connectToHost(m_Url.host(), m_Url.port());
        QThread::msleep(300);

        //FTP重连
        recvControl(220);
        excuteCmd("USER " + m_userName);
        recvControl(331);
        excuteCmd("PASS " + m_password);
        recvControl(230);
        intoPasv();
        excuteCmd("TYPE I");
        recvControl(200);
        excuteCmd("MODE S");
        recvControl(200);

        std::string restStr = std::string("REST "+ std::to_string(m_StartPoint + m_HaveDoneBytes));
        std::string retrStr = std::string("RETR "+ m_Url.path().toStdString());
        excuteCmd(restStr); // 断点续传
        recvControl(350);
        excuteCmd(retrStr);
        recvControl(150);

        connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(onReplyReadyRead()));
        connect(m_commandSocket, SIGNAL(readyRead()), this, SLOT(onControlReadyRead()));
    }
}

// 检测被动模式下 数据Socket是否断开，断开则重连
void FtpDownloader::onControlReadyRead()
{
    // 检测被动模式下 数据Socket是否断开，断开则重连
    m_commandSocket->read(buf, BUFLEN);
    if(getStateCode() == 226 && m_HaveDoneBytes < m_EndPoint - m_StartPoint - 1)
    {
        m_dataSocket->close();
        m_dataSocket->deleteLater();
        //dataSocket重新连接读取
        intoPasv();
        std::string restStr = std::string("REST "+ std::to_string(m_StartPoint + m_HaveDoneBytes));
        std::string retrStr = std::string("RETR "+ m_Url.path().toStdString());
        //确定读取字节位置
        excuteCmd(restStr);
        recvControl(350);
        excuteCmd(retrStr);
        recvControl(350);
        connect(m_dataSocket, SIGNAL(readyRead()), this, SLOT(onReplyReadyRead()));
    }
}

//写入数据
void FtpDownloader::onReplyReadyRead()
{
    if(!m_File)
        return;
        //qint64 size = m_dataSocket->read(databuf, DATABUFLEN);
        QByteArray byteArray = m_dataSocket->readAll();
        int size =byteArray.size();
        if(size <= 0)
        {
            //qDebug() << m_Index<<"="<< size;
            return;
        }
        //最后一次剩余字节处理
        if((m_StartPoint != 0 && size + m_HaveDoneBytes > m_EndPoint - m_StartPoint + 1) || (m_StartPoint == 0 && size + m_HaveDoneBytes > m_EndPoint))
        {
            size = m_EndPoint - m_StartPoint + 1 - m_HaveDoneBytes;
        }
        fileMutex.lock();
        //找到写入位置
        m_File->seek(m_StartPoint + m_HaveDoneBytes);
        m_File->write(byteArray);
        m_File->flush();
        saveDownload((m_Index-1)*2+5,QString::number(m_StartPoint +size+m_HaveDoneBytes).toStdString(),  size);
        fileMutex.unlock();
        m_HaveDoneBytes += size;

        //qDebug() <<"part"<< m_Index << " : readyReadh: havedone:" << m_HaveDoneBytes + m_StartPoint;
        if(m_HaveDoneBytes + m_StartPoint == m_EndPoint || m_HaveDoneBytes + m_StartPoint - 1 == m_EndPoint|| m_HaveDoneBytes + m_StartPoint + 1 == m_EndPoint)
        {
            //下载完成
            onReplyFinished();
        }
        emit updateCurrentSize(m_Index, size);

}
// 要编辑的行数，从1开始计数,"countSizer" 已下载总数据,new_star每个线程已下载数据
void FtpDownloader::saveDownload(int line_to_edit,std::string new_star,std::int64_t countSize)
{
        // 读取文件中的所有数据
        std::vector<std::string> lines;
        std::ifstream fin(m_outputFile);
        if (fin) {
            std::string line;
            while (getline(fin, line)) {
                lines.push_back(line);
            }
            fin.close();
        } else {
            qDebug() << "Failed to open file: " ;

        }
        // 检查要编辑的行数是否合法
        if (line_to_edit < 1 || line_to_edit > lines.size()) {
            qDebug() << "Invalid line number: " ;

        }
        // 在指定行删除旧数据，插入新数据
        lines[line_to_edit - 1] = new_star;
        std::int64_t tmp=countSize+std::stoi(lines[3]);

        lines[3]=QString::number(tmp).toStdString();
        // 将修改后的数据写回文件
        std::ofstream fout(m_outputFile);
        if (fout) {
            for (const std::string& line : lines) {
                fout << line << '\n';
            }
            fout.close();
           // qDebug() << "File updated successfully!";
        } else {
            //qDebug() << "Failed to open file: " ;

        }
}

//下载完成删除套接字
void FtpDownloader::onReplyFinished()
{
    qDebug() << "Part" << m_Index << "download finished:"
             << m_StartPoint << ", " << m_EndPoint;
    isDone = true;
    fileMutex.lock();
    m_File->flush();
    fileMutex.unlock();
    m_File = nullptr;
    m_dataSocket->deleteLater();
    m_commandSocket->deleteLater();
    //唤醒线程下载完成信号
    emit DownloadFinished(m_Index);
}


